import Tips from "./Tips";
import Tools from "./Tools";
/**
 * 绘制 点，线，面，圆，矩形，多边形
 */
export default class Draw{
    constructor(viewer) {
        this.viewer = viewer;
        this.camera = viewer.camera;
        this.scene = viewer.scene;
        this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.canvas);

        // 鼠标提示框
        this.tips = new Tips();

        this.tools = new Tools();

        // 点的三维坐标 数组 Cartesian3
        this.positions = [];

        // 点的 entities
        this.pointEnties = [];

        // 浮动的点
        this.floatingPoint = null;

        // 浮动的线
        this.floatDashLine = null;

        // 线段的 entity
        this.lineEntity = null;

        // 形状（面）的 entity
        this.shapeEntity = null;
    }

    // 清除鼠标事件
    clearInputAction() {
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);

        this.tips.hide();

        // 移除entity
        // 移除浮动的线
        if(this.floatDashLine){
            this.viewer.entities.remove(this.floatDashLine)
        }
        // 移除点的 entity
        this.pointEnties.forEach(item=>{
            this.viewer.entities.remove(item)
        });
        // 移除线
        this.viewer.entities.remove(this.lineEntity);
        // 移除面
        this.viewer.entities.remove(this.shapeEntity)

        // 清除所有定义好的字段
        this.positions = [];
        this.pointEnties = [];
        this.floatingPoint = null;
        this.floatDashLine = null;
        this.lineEntity = null;
        this.shapeEntity = null;
        
    }

    /**
     * 标绘， 绘制点
     */
    pointStart(opt) {
        this.tips.changeText('单击开始绘制');

        // 默认参数
        if (this.tools.nullBool(opt)) {
            opt = {
                point: {
                    color: new Cesium.Color.fromCssColorString('#d7e30f').withAlpha(0.5),
                    pixelSize: 5,
                    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
                }
            }
        }

        // 单击事件
        this.handler.setInputAction((event) => {
            // 返回在椭球上面的点的坐标
            let earthPosition = this.camera.pickEllipsoid(event.position, this.scene.globe.ellipsoid);
            if (Cesium.defined(earthPosition)) {
                // 在点击位置添加一个点
                let point = this.viewer.pointEnties.add(Object.assign({}, opt, { position: earthPosition }))
                this.pointEnties.push(point)
            }
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

        // 移动鼠标
        this.handler.setInputAction((event) => {
            if (document.documentElement.style.cursor === 'crosshair') {
                this.tips.show(event.endPosition);
            }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    }

    // 点 结束绘制
    pointEnd() {
        let entities = [...this.pointEnties];
        this.clearInputAction();
        return entities;
    }

    // 绘制最后两个点的连线，用于做浮动的线
    createFloatPolylineDash() {
        let pointLast2 = [this.positions[this.positions.length-2], this.positions[this.positions.length-1]]
        let dynamicPositions = new Cesium.CallbackProperty(() => {
            return pointLast2;
        }, false);
        return this.viewer.entities.add({
            polyline: {
                positions: dynamicPositions,
                width: 2,
                material: new Cesium.PolylineDashMaterialProperty({
                    color: Cesium.Color.fromCssColorString('#d7e30f').withAlpha(0.5),
                    dashLength: 20
                }),
                zIndex: 99 
            },
        });
    }

    // 创建连续的线段
    createLine(positions){
        // 获取动态的点位信息
        let dynamicPositions = new Cesium.CallbackProperty(() => {
            return positions;
        }, false);
        return this.viewer.entities.add({
            name:'line',
            polyline: {
                positions: dynamicPositions,
                width: 2,
                material: new Cesium.ColorMaterialProperty( 
                    Cesium.Color.fromCssColorString('#d7e30f').withAlpha(0.5)
                ),
                zIndex: 99 
            },
        });
    }

    // 创建多边形
    createShape(positions){
        // 获取动态的点位信息
        let dynamicHierarchy = new Cesium.CallbackProperty(
            function () {
                return new Cesium.PolygonHierarchy(
                    positions
                );
            },
            false
        );
        let shape = this.viewer.entities.add({
            polygon: {
                hierarchy: dynamicHierarchy,
                material: new Cesium.ColorMaterialProperty(
                    Cesium.Color.fromCssColorString('#d7e30f').withAlpha(0.5)
                ),
            },
        });
        return shape;
    }

    // 绘制多线段
    lineStart(opt) {
        this.tips.changeText('单击开始绘制，右键取消上一个点，双击结束绘制');
        
        // 单击事件
        this.handler.setInputAction((event) => {
            // 返回在椭球上面的点的坐标
            let earthPosition = this.camera.pickEllipsoid(event.position, this.scene.globe.ellipsoid);
            if (Cesium.defined(earthPosition)) {
                // 在点击位置添加一个点
                let point = this.viewer.entities.add(Object.assign({}, opt, { position: earthPosition }))
                this.pointEnties.push(point);

                // 绘制浮动的点
                this.floatingPoint = this.viewer.entities.add(Object.assign({}, opt, { position: earthPosition }))

                // 绘制浮动的点和上一次绘制的点的连线

                // let dynamicPositions = new Cesium.CallbackProperty(function () {
                //     return new Cesium.PolygonHierarchy(activeShapePoints);
                // }, false);
                // activeShape = drawShape(dynamicPositions);
            }
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

        // 移动鼠标
        this.handler.setInputAction((event) => {
            if (document.documentElement.style.cursor === 'crosshair') {
                this.tips.show(event.endPosition)
            }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    }

    /**
     * 绘制线段，只绘制一根
     */
    lineSegment(){
        this.tips.changeText('单击开始绘制');
        let pointOpt = {
            point: {
                color: new Cesium.Color.fromCssColorString('#d7e30f').withAlpha(0.5),
                pixelSize: 5,
            }
        }

        return new Promise(resolve=>{
            // 单击事件
            this.handler.setInputAction((event) => {
                // 返回在椭球上面的点的坐标
                let earthPosition = this.getCatesian3FromPX(event.position);
                if (Cesium.defined(earthPosition)) {
                    // 在点击位置添加一个点
                    let point = this.viewer.entities.add(Object.assign({}, pointOpt, { position: earthPosition }))
                    this.pointEnties.push(point);
                    this.positions.push(earthPosition);

                    // 第一次点击的时候多给一个点
                    if(this.positions.length===1){
                        this.positions.push(earthPosition);
                    }

                    // 绘制浮动的点
                    this.floatingPoint = this.viewer.entities.add(Object.assign({}, pointOpt, { position: earthPosition }))
                    this.pointEnties.push(this.floatingPoint);
                    
                    // 移除前面绘制过的线，并创建新的线
                    if(this.lineEntity){
                        this.viewer.entities.remove(this.lineEntity)
                    }
                    let newPostions = [...this.positions];
                    newPostions.pop();
                    this.lineEntity = this.createLine(newPostions);

                    // 单点击两次（绘制出一条直线）的时候就取消绘制
                    if(this.positions.length===3){
                        this.positions.pop();
                        let positions = [...this.positions];
                        let entity = Cesium.clone(this.lineEntity);

                        // 清除鼠标事件
                        this.clearInputAction();

                        // 返回线的 实体 和 位置点
                        resolve({
                            entity,
                            positions
                        })
                    }
                }
            }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

            // 移动鼠标
            this.handler.setInputAction((event) => {
                if (document.documentElement.style.cursor === 'crosshair') {
                    this.tips.show(event.endPosition);
                    if(this.positions.length===2){
                        this.tips.changeText('再次点击结束绘制')
                    }
                }
                
                // 确保鼠标在地球上，并且已经点击过一次，有浮动点了
                if (!Cesium.defined(event.endPosition) || !this.floatingPoint) return;

                let earthPosition = this.getCatesian3FromPX(event.endPosition);
                if (!Cesium.defined(earthPosition)) return;
                this.floatingPoint.position.setValue(earthPosition);
                this.positions.pop();
                this.positions.push(earthPosition);


                // 绘制浮动的点和上一次绘制的点的连线
                if(this.floatDashLine){
                    this.viewer.entities.remove(this.floatDashLine)
                }
                this.floatDashLine = this.createFloatPolylineDash();
                
            }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
        })
    }

    /**
     * 拾取位置点，能够根据鼠标位置判断出是画在3dtils上，还是画在地球上，还是画在地形上
     * @param {Object} px 屏幕坐标
     * @return {Object} Cartesian3 三维坐标
     */
    getCatesian3FromPX(px) {
        if (this.viewer && px) {
            let picks = this.viewer.scene.drillPick(px);
            let cartesian = null;
            let isOn3dtiles = false,
                isOnTerrain = false;
            // drillPick
            for (let i in picks) {
                let pick = picks[i];

                if (
                    (pick && pick.primitive instanceof Cesium.Cesium3DTileFeature) ||
                    (pick && pick.primitive instanceof Cesium.Cesium3DTileset) ||
                    (pick && pick.primitive instanceof Cesium.Model)
                ) {
                    //模型上拾取
                    isOn3dtiles = true;
                }
                // 3dtilset
                if (isOn3dtiles) {
                    this.viewer.scene.pick(px); // pick
                    cartesian = this.viewer.scene.pickPosition(px);
                    if (cartesian) {
                        let cartographic = Cesium.Cartographic.fromCartesian(cartesian);
                        if (cartographic.height < 0) cartographic.height = 0;
                        let lon = Cesium.Math.toDegrees(cartographic.longitude),
                            lat = Cesium.Math.toDegrees(cartographic.latitude),
                            height = cartographic.height;
                        cartesian = this.transformWGS84ToCartesian({
                            lng: lon,
                            lat: lat,
                            alt: height,
                        });
                    }
                }
            }
            // 地形
            let boolTerrain = this.viewer.terrainProvider instanceof Cesium.EllipsoidTerrainProvider;
            // Terrain
            if (!isOn3dtiles && !boolTerrain) {
                let ray = this.viewer.scene.camera.getPickRay(px);
                if (!ray) return null;
                cartesian = this.viewer.scene.globe.pick(ray, this.viewer.scene);
                isOnTerrain = true;
            }
            // 地球
            if (!isOn3dtiles && !isOnTerrain && boolTerrain) {
                cartesian = this.viewer.scene.camera.pickEllipsoid(
                    px,
                    this.viewer.scene.globe.ellipsoid
                );
            }
            if (cartesian) {
                let position = this.transformCartesianToWGS84(cartesian);
                if (position.alt < 0) {
                    cartesian = this.transformWGS84ToCartesian(position, 0.1);
                }
                return cartesian;
            }
            return false;
        }
    }

    /***
     * 坐标转换 笛卡尔转84
     * @param {Object} Cartesian3 三维位置坐标
     * @return {Object} {lng,lat,alt} 地理坐标
     */
    transformCartesianToWGS84(cartesian) {
        if (cartesian) {
            var ellipsoid = Cesium.Ellipsoid.WGS84;
            var cartographic = ellipsoid.cartesianToCartographic(cartesian);
            return {
                lng: Cesium.Math.toDegrees(cartographic.longitude),
                lat: Cesium.Math.toDegrees(cartographic.latitude),
                alt: cartographic.height,
            };
        }
    }

    /***
     * 坐标转换 84转笛卡尔
     * @param {Object} {lng,lat,alt} 地理坐标
     * @return {Object} Cartesian3 三维位置坐标
     */
    transformWGS84ToCartesian(position, alt) {
        return position ?
            Cesium.Cartesian3.fromDegrees(
                position.lng || position.lon,
                position.lat,
                position.alt = alt || position.alt,
                Cesium.Ellipsoid.WGS84
            ) :
            Cesium.Cartesian3.ZERO
    }
    
    /**
     * 
     * @returns Promise then 绘制entity
     */
    ploygon() {
        this.tips.changeText('单击开始绘制，双击结束绘制，右键取消点位');
        return new Promise((resolve) => {
            // 单击事件
            this.handler.setInputAction((event) => {
                // 返回在椭球上面的点的坐标
                let earthPosition = this.getCatesian3FromPX(event.position);
                if (!Cesium.defined(earthPosition)) return;

                this.positions.push(earthPosition);

                // 第一次点击的时候多给一个点
                if(this.positions.length === 1){
                    this.positions.push(earthPosition);
                }
                
                // 绘制线
                if(this.lineEntity){
                    this.viewer.entities.remove(this.lineEntity)
                }
                this.lineEntity = this.createLine(this.positions);

                // 绘制面
                if(this.shapeEntity){
                    this.viewer.entities.remove(this.shapeEntity)
                }
                this.shapeEntity = this.createShape(this.positions);
                
            }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

            // 移动鼠标
            this.handler.setInputAction((event) => {
                if (document.documentElement.style.cursor === 'crosshair') {
                    this.tips.show(event.endPosition);
                }
                
                // 确保鼠标在地球上，并且已经点击过一次，有浮动点了
                if (!Cesium.defined(event.endPosition)) return;

                // 获取三维坐标
                let earthPosition = this.getCatesian3FromPX(event.endPosition);
                if (!Cesium.defined(earthPosition)) return;
                
                
                // 更新点位数组最后一个点的三维坐标
                this.positions.pop();
                this.positions.push(earthPosition);

                if(!this.lineEntity) return;

                if(this.positions.length>=3){
                    // 隐藏线
                    this.lineEntity.show = false;
                }else{
                    // 显示线
                    this.lineEntity.show = true;
                }

            }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

            // 鼠标右键
            this.handler.setInputAction((event) => {
                // 移除点
                this.positions.pop();
                
            }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)

            // 鼠标双击
            this.handler.setInputAction((event) => {
                this.positions.pop();
                this.positions.pop();

                let entity = Cesium.clone(this.shapeEntity)

                this.clearInputAction()

                resolve(entity)
                
            }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK)
        })
    }

    /**
     * 多边形点位拆分成多个三角形
     * @param {Array} positions 多边形点位
     * @returns [Array, Array, ...] 二位数组
     */
    ploygon2triangle(positions){
        if(positions.length<=3) return [positions];

        let _pArr = [];
        // 三角形个数
        let _allLen = positions.length;
        let _len = _allLen - 2;
        
        for(let i = 0; i < _len; i++){
            let _i0 = i;
            let _i1 = (i+1)%_allLen;
            let _i2 = (i+2)%_allLen;
            _pArr[i] = [positions[_i0], positions[_i1], positions[_i2]]
        }
        
        return _pArr
    }

    /**
     * 判断所有的点位是不是逆时针
     * @param {Array} arr 点位数组 [Cartesian3, ...] 或者 [{lat, lng}, ...]
     * @returns 逆时针的 [{lat, lng}, ...]
     */
    isClockWise(arr) {
        let latLngArr = [];
        // 将三维坐标的数组转经纬度的数组
        for (let i = 0; i < arr.length; i++) {
            if(arr[i].constructor === Cesium.Cartesian3){
                let temp = this.transformCartesianToWGS84(arr[i]);
                latLngArr.push(temp)
            }else{
                latLngArr.push(arr[i])
            }
        }

        if (latLngArr.length < 3) {
            return latLngArr;
        }

        if (latLngArr[0] === latLngArr[latLngArr.length - 1]) {
            latLngArr = latLngArr.slice(0, latLngArr.length - 1);
        }
        let latMin = { i: -1, val: 90 };
        for (let i = 0; i < latLngArr.length; i++) {
            let { lat } = latLngArr[i];
            if (lat < latMin.val) {
                latMin.val = lat;
                latMin.i = i;
            }
        }
        let i1 = (latMin.i + latLngArr.length - 1) % latLngArr.length;
        let i2 = latMin.i;
        let i3 = (latMin.i + 1) % latLngArr.length;

        let v2_1 = {
            lat: latLngArr[i2].lat - latLngArr[i1].lat,
            lng: latLngArr[i2].lng - latLngArr[i1].lng,
        };
        let v3_2 = {
            lat: latLngArr[i3].lat - latLngArr[i2].lat,
            lng: latLngArr[i3].lng - latLngArr[i2].lng,
        };
        let result = v3_2.lng * v2_1.lat - v2_1.lng * v3_2.lat;
        // result>0 3-2在2-1的顺时针方向 result<0 3-2在2-1的逆时针方向 result==0 3-2和2-1共线，可能同向也可能反向
        let bool = result === 0
            ? latLngArr[i3].lng < latLngArr[i1].lng
            : result > 0;

        return bool?latLngArr.reverse():latLngArr
    }

    // 创建裁剪平面
    createClippingPlanes(positions){
        let planes = [];
        let clockWisePositions = this.isClockWise(positions);
        for(let i = 0; i < clockWisePositions.length; i++){
            let nextIndex = (i + 1) % clockWisePositions.length;
            let itemDegrees = Cesium.Cartesian3.fromDegrees(
                clockWisePositions[i].lng,
                clockWisePositions[i].lat
            );
            let nextDegrees = Cesium.Cartesian3.fromDegrees(
                clockWisePositions[nextIndex].lng,
                clockWisePositions[nextIndex].lat
            );
            let midpoint = Cesium.Cartesian3.add(
                itemDegrees,
                nextDegrees,
                new Cesium.Cartesian3()
            );
            midpoint = Cesium.Cartesian3.multiplyByScalar(
                midpoint,
                0.5,
                midpoint
            );
            let up = Cesium.Cartesian3.normalize(
                midpoint,
                new Cesium.Cartesian3()
            );
            let right = Cesium.Cartesian3.subtract(
                nextDegrees,
                midpoint,
                new Cesium.Cartesian3()
            );
            right = Cesium.Cartesian3.normalize(right, right);
            let normal = Cesium.Cartesian3.cross(
                right,
                up,
                new Cesium.Cartesian3()
            );
            normal = Cesium.Cartesian3.normalize(normal, normal);
            let originCenteredPlane = new Cesium.Plane(normal, 0.0);
            let distance = Cesium.Plane.getPointDistance(
                originCenteredPlane,
                midpoint
            );
            console.log('distance', distance);
            planes.push(new Cesium.ClippingPlane(normal, distance));
        }

        let clippingPlanes = new Cesium.ClippingPlaneCollection({
            planes: planes,
            edgeWidth: 1.0,
            edgeColor: Cesium.Color.WHITE,
            // unionClippingRegions:true
        });

        return {clippingPlanes, clockWisePositions}
    }

}